stock bool:IsValidPlayer(client, bool:alive = false, bool:bots = false)
{
	if(0 < client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
	{
		if(!bots && IsFakeClient(client))
			PrintToConsole(client, "[DEBUG; TEMP DISABLED] %N bots!", client);
			//return false;
		
		if(alive && !IsPlayerAlive(client))
			return false;
		
		return true;
	}
	return false;
}

stock bool:IsClientLoaded(client)
{
	return ClientLoaded[client];
}

stock bool:IsConnectedToDB()
{
	return bIsConnectedToDB;
}

LoadAllPlayers()
{
	for(new client = 1; client <= MaxClients; client++)
	{
		if(!IsValidPlayer(client))
			continue;
		
		if(!IsClientLoaded(client))
			LoadPlayer(client);
	}
}

SaveAllPlayers()
{
	for(new client = 1; client <= MaxClients; client++)
	{
		if(!IsValidPlayer(client))
			continue;
		
		SavePlayer(client);
	}
}

ResetVariables(client)
{
	ClientLoaded[client] = false;
	SetLevel(client, 1);
	SetXP(client, 0);
	SetReqXP(client, GetConfigValue("xp", "exp_start"));
	SetCredits(client, GetConfigValue("xp", "credits_start"));
	for(new i = 0; i < GetSkillCount(); i++)
		SetSkillLevel(client, i, 0);
	if(IsValidPlayer(client))
		SetMaxHP(client, 100);
}

GetLevel(client)
{
	return PlayerLevel[client];
}

GetXP(client)
{
	return PlayerXP[client];
}

GetReqXP(client)
{
	return PlayerReqXP[client];
}

GetCredits(client)
{
	return PlayerCredits[client];
}

SetLevel(client, level)
{
	PlayerLevel[client] = level;
	new reqxp = (level-1)*GetConfigValue("xp", "exp_inc")+GetConfigValue("xp", "exp_start");
	SetReqXP(client, reqxp);
	SetXP(client, 0);
	//SetCredits(client, GetCredits(client)+GetConfigValue("xp", "credits_inc"));
	return PlayerLevel[client];
}

SetXP(client, xp)
{
	PlayerXP[client] = xp;
	return PlayerXP[client];
}

SetReqXP(client, xp)
{
	PlayerReqXP[client] = xp;
	return PlayerReqXP[client];
}

SetCredits(client, credits)
{
	PlayerCredits[client] = credits;
	return PlayerCredits[client];
}

SetSkillLevel(client, skillid, level)
{
	if(skillid > -1 && skillid < GetSkillCount())
	{
		PlayerSkill[client][skillid] = level;
		return PlayerSkill[client][skillid];
	}
	
	return -1;
}

GetSkillLevel(client, skillid)
{
	if(skillid > -1 && skillid < GetSkillCount())
		return PlayerSkill[client][skillid];
	
	return -1;
}

GetSkillCount()
{
	return CountSkills;
}

ResetSkillCount()
{
	CountSkills = 0;
	return CountSkills;
}

GetSkillShortname(skillid, String:buffer[], maxlen)
{
	if(skillid > -1 && skillid < GetSkillCount())
		Format(buffer, maxlen, "%s", SkillShortname[skillid]);
}

GetSkillName(skillid, String:buffer[], maxlen, client = 0)
{
	if(skillid > -1 && skillid < GetSkillCount())
	{
		Format(buffer, maxlen, "%s_Name", SkillShortname[skillid]);
		Format(buffer, maxlen, "%T", buffer, client);
	}
}

GetSkillDesc(skillid, String:buffer[], maxlen, client = 0)
{
	if(skillid > -1 && skillid < GetSkillCount())
	{
		Format(buffer, maxlen, "%s_Desc", SkillShortname[skillid]);
		Format(buffer, maxlen, "%T", buffer, client);
	}
}

GetSkillMaxLevel(skillid)
{
	if(skillid > -1 && skillid < GetSkillCount())
		return SkillMaxLevel[skillid];
	
	return -1;
}

GetClientSkillCost(client, skillid)
{
	if(skillid > -1 && skillid < GetSkillCount())
		return SkillCost[skillid]+SkilliCost[skillid]*GetSkillLevel(client, skillid);
	
	return -1;
}

GetSkillCost(skillid, level = 0)
{
	if(skillid > -1 && skillid < GetSkillCount())
	{
		if(level == 0)
			return SkillCost[skillid];
		else
			return SkillCost[skillid]+SkilliCost[skillid]*level;
	}
	
	return -1;
}

GetConfigValue(const String:key1[], const String:key2[], defvalue = 0)
{
	if(hConfig == INVALID_HANDLE)
		if(LoadConfig() == INVALID_HANDLE)
		{
			LogError("Could not load config: \"configs/ncs_rpg.txt\"");
			return -1;
		}
		
	KvRewind(hConfig);
	if(KvJumpToKey(hConfig, key1))
		return KvGetNum(hConfig, key2, defvalue);
	
	return -1;
}

Float:GetConfigValueFloat(const String:key1[], const String:key2[], Float:defvalue = 0.0)
{
	if(hConfig == INVALID_HANDLE)
		if(LoadConfig() == INVALID_HANDLE)
		{
			LogError("Could not load config: \"configs/ncs_rpg.txt\"");
			return -1.0;
		}
	
	KvRewind(hConfig);
	if(KvJumpToKey(hConfig, key1))
		return KvGetFloat(hConfig, key2, defvalue);
	
	return -1.0;
}

bool:GetConfigValueString(const String:key1[], const String:key2[], String:value[], maxlength, const String:defvalue[] = "")
{
	if(hConfig == INVALID_HANDLE)
		if(LoadConfig() == INVALID_HANDLE)
		{
			LogError("Could not load config: \"configs/ncs_rpg.txt\"");
			return false;
		}
	
	KvRewind(hConfig);
	if(KvJumpToKey(hConfig, key1))
	{
		KvGetString(hConfig, key2, value, maxlength, defvalue);
		return true;
	}
	
	return false;
}

SetMaxHP(client, amount)
{
	iMaxHP[client] = amount;
	new hp = GetMaxHP(client);
	if(GetClientHealth(client) > hp)
		SetEntityHealth(client, hp);
	return hp;
}

GetMaxHP(client)
{
	return iMaxHP[client];
}

SetColor(entity, r, g, b, a = -1)
{
	if(r != -1)
		SetEntData(entity, m_clrRender, r, 1, true);
	if(g != -1)
		SetEntData(entity, m_clrRender+1, g, 1, true);
	if(b != -1)
		SetEntData(entity, m_clrRender+2, b, 1, true);
	if(a != -1)
		SetEntData(entity, m_clrRender+3, a, 1, true);
	
	SetEntityRenderMode(entity, RENDER_TRANSTEXTURE);
}

GetAlpha(entity)
{
	return GetEntData(entity, m_clrRender+3, 1);
}

DealDamage(victim, damage, attacker = -1, dmg_type = DMG_GENERIC, String:weapon[] = "")
{
	if(IsValidPlayer(victim, true) && damage > 0)
	{
		new ent = CreateEntityByName("point_hurt");
		if(ent != -1)
		{
			decl String:buffer[8];
			DispatchKeyValue(victim, "targetname", "rpg_hurtme");
			DispatchKeyValue(ent, "DamageTarget", "rpg_hurtme");
			
			IntToString(damage, buffer, sizeof(buffer));
			DispatchKeyValue(ent, "Damage", buffer);
			
			IntToString(dmg_type, buffer, sizeof(buffer));
			DispatchKeyValue(ent, "DamageType", buffer);
			
			if(strlen(weapon) > 0)
				DispatchKeyValue(ent, "classname", weapon);
			
			DispatchSpawn(ent);
			AcceptEntityInput(ent, "Hurt", attacker);
			DispatchKeyValue(victim, "targetname", "rpg_donthurtme");
			RemoveEdict(ent);
		}
	}
}

GetActiveWeapon(client)
{
	if(IsValidPlayer(client, true))
		return GetEntPropEnt(client, Prop_Data, "m_hActiveWeapon");
	
	return -1;
}

bool:ShakePlayer(client, Float:flAmplitude, Float:time, Float:noise = 1.0)
{
	if(IsValidPlayer(client))
	{
		new Handle:bf = StartMessageOne("Shake", client);
		if(bf != INVALID_HANDLE)
		{
			BfWriteByte(bf, 0);				// ???
			BfWriteFloat(bf, flAmplitude);	// magnitude/amplitude
			BfWriteFloat(bf, noise);		// noise frequency
			BfWriteFloat(bf, time);			// time
			EndMessage();
			return true;
		}
	}
	return false;
}

// Speed ------------------------------------------------------------------------------------------------------------------
// setslow
Handle:SetFreeze(client, Float:time)
{
	if(hTimerFreeze[client] != INVALID_HANDLE)
	{
		KillTimer(hTimerFreeze[client]);
		hTimerFreeze[client] = INVALID_HANDLE;
	}
	
	if(time <= 0.0)
	{
		fFreeze[client] = time;
		SetSpeed(client, fPlayerSpeed[client], false);
	}
	else
	{
		SetSpeed(client, 0.0, false);
		fFreeze[client] = time;
		hTimerFreeze[client] = CreateTimer(0.1, Timer_Unfreeze, client, TIMER_REPEAT|TIMER_FLAG_NO_MAPCHANGE);
		return hTimerFreeze[client];
	}
	
	return INVALID_HANDLE;
}

Float:GetFreezeTime(client)
{
	return fFreeze[client];
}

public Action:Timer_Unfreeze(Handle:timer, any:client)
{
	if(IsValidPlayer(client, true))
	{
		fFreeze[client] -= 0.1;
		if(fFreeze[client] > 0.0)
			return Plugin_Continue;
		
		SetFreeze(client, 0.0);
		return Plugin_Stop;
	}
	
	hTimerFreeze[client] = INVALID_HANDLE;
	return Plugin_Stop;
}

Float:SetSpeed(client, Float:amount, bool:variable = true)
{
	if(variable)
		fPlayerSpeed[client] = amount;
	
	if(GetFreezeTime(client) > 0.0)
		return 0.0;
	
	SetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue", amount-GetSlow(client));
	return GetSpeed(client);
}

Float:GetSpeed(client)
{
	new Float:amount;
	if(GetFreezeTime(client) > 0.0)
		amount = fPlayerSpeed[client];
	else
		amount = GetEntPropFloat(client, Prop_Data, "m_flLaggedMovementValue")+GetSlow(client);
	return amount;
}

Float:SetMaxSpeed(client, Float:amount)
{
	fMaxSpeed[client] = amount;
	new Float:speed = GetMaxSpeed(client);
	if(GetSpeed(client) > speed)
		SetSpeed(client, speed);
	return GetMaxSpeed(client);
}

Float:GetMaxSpeed(client)
{
	return fMaxSpeed[client];
}

Float:SetSlow(client, Float:amount)
{
	fPlayerSlow[client] = amount;
	SetSpeed(client, fPlayerSpeed[client], false);
	return GetSlow(client);
}

Float:GetSlow(client)
{
	return fPlayerSlow[client];
}